﻿/******************************************************************************
* SunnyUI.FrameDecoder 开源TCP、串口数据解码库。
* CopyRight (C) 2012-2023 ShenYongHua(沈永华).
* QQ群：56829229 QQ：17612584 EMail：SunnyUI@qq.com
*
* Blog:   https://www.cnblogs.com/yhuse
* Gitee:  https://gitee.com/yhuse/SunnyUI
* GitHub: https://github.com/yhuse/SunnyUI
*
* SunnyUI.dll can be used for free under the MulanPSL2 license.
* If you use this code, please keep this note.
* 如果您使用此代码，请保留此说明。
******************************************************************************
* 文件名称: UdpReceiver.cs
* 文件说明: UDP扩展类
* 当前版本: V1.0
* 创建日期: 2022-11-01
*
* 2022-11-01: V1.0.0 增加文件说明
******************************************************************************/

using SunnyUI.FrameDecoder;
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace SunnyUI.Core
{
    /// <summary>
    /// UDP数据接收类
    /// </summary>
    public class UdpReceiver : IDisposable
    {
        /// <summary>
        /// Udp端口
        /// </summary>
        public int Port { get; set; }

        private UdpClient client;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="port">端口</param>
        /// <param name="bufferSize">接收缓冲大小</param>
        public UdpReceiver(int port, int bufferSize = 64 * 1024)
        {
            Port = port;
            IPEndPoint localIPEndPoint = new IPEndPoint(IPAddress.Any, port);//设置ip和端口
            client = new UdpClient(localIPEndPoint) { Client = { ReceiveBufferSize = bufferSize } };
        }

        /// <summary>
        /// 通讯套接字
        /// </summary>
        public Socket Client => client.Client;

        Thread threadReceive;

        /// <summary>
        /// 开启接收数据线程
        /// </summary>
        public void Start()
        {
            IsStart = true;
            threadReceive = new Thread(ReceiveMessages);//线程回调函数为ReceiveMessages
            threadReceive.IsBackground = true;
            threadReceive.Start();//开启
        }

        /// <summary>
        /// 停止接收数据线程
        /// </summary>
        public void Stop()
        {
            IsStart = false;
        }

        /// <summary>
        /// 接收数据事件
        /// </summary>
        public event OnUdpByteEventHandler OnReceive;

        /// <summary>
        /// 接收线程是否开启
        /// </summary>
        public bool IsStart { get; private set; }

        private void ReceiveMessages()//线程回调函数
        {
            while (IsStart)
            {
                try
                {
                    if (client.Available > 0 && client.Client != null)
                    {
                        IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 0);
                        byte[] receiveBytes = client.Receive(ref endPoint);

                        if (decoderFunc == null)
                        {
                            OnReceive?.Invoke(this, new UdpByteDataEventArgs(endPoint, null, receiveBytes));
                        }
                        else
                        {
                            if (!DecoderCache.ContainsKey(endPoint))
                            {
                                var decoder = decoderFunc.Invoke();
                                decoder.IPEndPoint = endPoint;
                                decoder.OnDecoder += Decoder_OnDecoder;
                                DecoderCache.Add(endPoint, decoder);
                            }

                            DecoderCache[endPoint].Decode(receiveBytes);
                        }
                    }
                    else
                    {
                        Thread.Sleep(ThreadSleepInterval);
                    }
                }
                catch
                {
                }
            }
        }

        private void Decoder_OnDecoder(object sender, IByteEventArgs e)
        {
            OnReceive?.Invoke(this, new UdpByteDataEventArgs(e.Decoder.IPEndPoint, e.Decoder, e.Value));
        }

        /// <summary>
        /// 线程休眠间隔
        /// </summary>
        public int ThreadSleepInterval { get; set; } = 10;

        /// <summary>
        /// 析构函数
        /// </summary>
        public void Dispose()
        {
            Stop();
            client.Close();
        }

        private Func<BaseByteFrameDecoder> decoderFunc = null;

        /// <summary>
        /// 设置解码器
        /// </summary>
        /// <param name="decoder">创建解码器</param>
        /// <param name="aliveMinutes">解码器超时清空，默认1分钟</param>
        public void SetFrameDecoder(Func<BaseByteFrameDecoder> decoder, int aliveMinutes = 1)
        {
            decoderFunc = decoder;

            DecoderCache?.Dispose();
            DecoderCache = new DecoderCache<IPEndPoint, BaseByteFrameDecoder>(aliveMinutes);
        }

        private DecoderCache<IPEndPoint, BaseByteFrameDecoder> DecoderCache;
    }
}
